from tempfile import NamedTemporaryFile

import pandas as pd
from django.conf import settings
from django.http import FileResponse
from django.utils.encoding import escape_uri_path
from rest_framework.views import Response, status
from rest_framework import viewsets, mixins
from rest_framework.filters import OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import action
from django.db.models import F

from utils.common import Pagination
# from apps.survey_system.file.models import FileHistory
from .models import QuestionCategory, Question, Module, QuestionOption, \
    Questionnaire, QuestionnaireQsCate, QuestionnaireQuestion, Swiper, ModuleTeam
from .filters import QuestionCategoryFilter, QuestionFilter, QuestionnaireFilter, ModuleTeamFilter
from .serializers import QuestionCategorySerializer, QuestionCreateSerializer, QuestionListSerializer, \
    QuestionUpdateSerializer, ModuleSerializer, QuestionOptionsSerializer, \
    QuestionnaireListSerializer, QuestionnaireCreateSerializer, QuestionnaireRetrieveSerializer, \
    QuestionnaireUpdateSerializer, QuestionnaireDetailSerializer, SwiperSerializer, ModuleTeamSerializer


class ModuleViewSet(viewsets.GenericViewSet,
                    mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin):
    queryset = Module.objects.all()
    serializer_class = ModuleSerializer

    @action(methods=['get'], detail=False, url_path='menu')
    def get_menu(self, request, pk=None):
        queryset = self.get_queryset()
        res_data = []
        for i in queryset:
            tmp = {
                "path": f"/{i.codename}_{i.id}",
                "name": f"/{i.codename}_{i.id}_manage",
                "title": i.name,
                "icon": 'el-icon-edit-outline',
                "codename": i.codename,
                "id": i.id
            }
            res_data.append(tmp)
        return Response(res_data)

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()

        # 判断权限
        if not request.user.is_admin():
            module_codename = instance.module.codename
            if not request.user.had_permission(f'module_manage_{module_codename}'):
                return Response({'message': '无权限进行该操作'})

        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        return Response(serializer.data)


# 问题分类
class QuestionCategoryViewSet(mixins.ListModelMixin,
                              mixins.CreateModelMixin,
                              mixins.DestroyModelMixin,
                              mixins.UpdateModelMixin,
                              viewsets.GenericViewSet):
    queryset = QuestionCategory.objects.filter(is_active=True)
    filter_backends = (OrderingFilter, DjangoFilterBackend)
    serializer_class = QuestionCategorySerializer
    filter_class = QuestionCategoryFilter
    permission_classes = [IsAuthenticated]
    pagination_class = Pagination
    ordering_fields = ('id',)

    def filter_queryset(self, queryset):
        """ 动态使用 filter_class """
        if self.action in ['list']:
            for backend in list(self.filter_backends):
                queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        instance.is_active = False
        Question.objects.filter(category_id=instance.id).update(is_active=False)
        instance.save()
        return Response(status=status.HTTP_204_NO_CONTENT)


# 问题
class QuestionViewSet(mixins.CreateModelMixin,
                      mixins.ListModelMixin,
                      mixins.DestroyModelMixin,
                      mixins.UpdateModelMixin,
                      viewsets.GenericViewSet):
    queryset = Question.objects.filter(is_active=True).select_related('category')
    # permission_classes = [IsAuthenticated]
    filter_class = QuestionFilter

    # def destroy(self, request, *args, **kwargs):
    #     """ 删除题目后，将该题目的相关文件设置为冗余 """
    #     instance = self.get_object()
    #     file_path_lst = [i['url'] for i in instance.file] if instance.file else []
    #     question_options_files = QuestionOption.objects.filter(question_id=instance.id).values_list('file', flat=True)
    #     for file in question_options_files:
    #         if not file:
    #             continue
    #         file_path_lst.extend([i['url'] for i in file])
    #     self.perform_destroy(instance)
    #     FileHistory.objects.filter(path__in=file_path_lst).update(need_delete=True)  # 设置文件为冗余
    #     return Response(status=status.HTTP_204_NO_CONTENT)

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()

        # 判断权限
        if not request.user.is_admin():
            module_codename = instance.module.codename
            if not request.user.had_permission(f'survey_question_delete_{module_codename}'):
                return Response({'message': '无权限进行该操作'})

        instance.is_active = False
        instance.save()
        return Response(status=status.HTTP_204_NO_CONTENT)

    def get_serializer_class(self):
        if self.action == 'create':
            return QuestionCreateSerializer
        if self.action == 'update' or self.action == 'partial_update':
            return QuestionUpdateSerializer
        return QuestionListSerializer

    def filter_queryset(self, queryset):
        """ 动态使用 filter_class """
        if self.action in ['list']:
            for backend in list(self.filter_backends):
                queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 判断权限
        if not request.user.is_admin():
            module_id = serializer.initial_data['module']
            module_codename = Module.objects.filter(id=module_id).values_list('codename', flat=True).first()
            if not request.user.had_permission(f'survey_question_create_{module_codename}'):
                return Response({'message': '无权限进行该操作'})

        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()

        # 判断权限
        if not request.user.is_admin():
            module_codename = instance.module.codename
            if not request.user.had_permission(f'survey_question_update_{module_codename}'):
                return Response({'message': '无权限进行该操作'})

        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        return Response(serializer.data)

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        queryset = queryset.order_by('category__name', 'created_time')
        serializer = QuestionListSerializer(queryset, many=True)
        qs_id = [i['id'] for i in serializer.data]
        options_qs = QuestionOption.objects.filter(question_id__in=qs_id)
        options_data = QuestionOptionsSerializer(options_qs, many=True).data
        options_dict = dict()  # key 为题目id, value 为该题目选项列表
        for i in options_data:
            if i['question_id'] in options_dict:
                options_dict[i['question_id']].append(i)
            else:
                options_dict[i['question_id']] = [i]
        question_list = []

        # 构建题目分类树
        question_tree = dict()
        for qs in serializer.data:
            category_name = qs['category_name']
            qs['options'] = options_dict.get(qs['id'], [])
            if category_name not in question_tree:
                question_tree[category_name] = [qs]
            else:
                question_tree[category_name].append(qs)
        question_tree_lst = []
        for category_name, question in question_tree.items():
            question_tree_lst.append({'label': category_name, 'children': question, 'id': category_name})
            question_list.extend(question)

        res_data = {'question_tree_lst': question_tree_lst, 'question_list': question_list}
        return Response(res_data, status=status.HTTP_200_OK)

    @action(methods=['get'], detail=False, url_path='single_questions_template')
    def get_single_questions_template(self, request, pk=None):
        """ 返回单选题的导入模板 """
        file_full_path = str(settings.ROOT_DIR) + '/fixtures/survey_system/批量导入单选题题目格式.xlsx'
        file_name = "批量导入单选题模板.xlsx"
        with NamedTemporaryFile() as tmp:
            file = open(file_full_path, 'rb')
            response = FileResponse(file)
            response['Content-Type'] = "application/octet-stream"
            response['Content-Disposition'] = f"attachment;filename={escape_uri_path(file_name)}"
            response['Access-Control-Expose-Headers'] = 'Content-Disposition'
            return response

    @action(methods=['post'], detail=False, url_path='upload_single_questions')
    def upload_questions(self, request, pk=None):
        """
        根据上传的excel表，录入单选题题目(只录入不更新)。
        :param category_id: 要录入的题目分类的id，必须。
        :param module_id: 要录入的模块的id，必须。
        :param upload: 上传的excel文件, excel表字段需要：题型，题目，是否必填，满分分值
        """
        category_id = request.data.get('category_id', '')  # 获取要录入的题目分类的id
        module_id = request.data.get('module_id', '')  # 获取要录入的模块的id
        excel_file = request.data.get('upload', '')
        if not excel_file:
            return Response({"message": "请上传excel文件"}, status=status.HTTP_400_BAD_REQUEST)
        df = pd.read_excel(excel_file, dtype=str, keep_default_na=False)
        # if len(df) >= 100:
        #     return Response({'message': '请不要一次性录入超过100条数据'}, status=status.HTTP_400_BAD_REQUEST)

        columns_lst = list(df.columns)
        df.columns = [column.replace(' ', '') for column in columns_lst]  # 对列名进行格式化，去掉空格
        columns_set = set(df.columns)
        require_columns = {'题目', '是否必填', '满分分值'}
        if not require_columns.issubset(columns_set):  # 判断需要的列名是否存在于传过来的excel表中
            return Response({'message': '请按照模版填写正确的列名'}, status=status.HTTP_400_BAD_REQUEST)
        for column in require_columns:
            df[column] = df[column].str.replace(' ', '')  # 对数据进行格式化，去掉空格

        # 按题目分类录入，需要判断要录入的班级和传进来的excel表中的班级是否一致
        category_obj = QuestionCategory.objects.select_related('module') \
            .filter(id=category_id).first()
        if not category_obj:
            return Response({'message': '请提供正确的题目分类id'}, status=status.HTTP_400_BAD_REQUEST)

        # 先判断需要的信息是否都有填写
        for row in df.itertuples():
            row_index = getattr(row, 'Index') + 2

            question = getattr(row, '题目')
            if not all([question]):  # 只有全部有数据才能进行下一步
                err_msg = f'第 {row_index} 行出错：题目是必须填写的。'
                return Response({'message': err_msg}, status=status.HTTP_400_BAD_REQUEST)

            is_require, score = getattr(row, '是否必填'), getattr(row, '满分分值')
            if is_require not in ['是', '否']:
                return Response({'message': f'第 {row_index} 行出错: 是否必填只能填是或否'},
                                status=status.HTTP_400_BAD_REQUEST)
            if int(score) < 0:
                return Response({'message': f'第 {row_index} 行出错: 满分分值要大于等于0'},
                                status=status.HTTP_400_BAD_REQUEST)
            cur_index_data = df.values[row_index - 2]
            cur_len = len(cur_index_data)
            columns_index = 3
            options = df.values[row_index - 2, columns_index]
            content = None
            while options:
                options_score = df.values[row_index - 2, columns_index + 1]
                if options_score:
                    if int(options_score) >= 0:
                        if int(options_score) > int(score):
                            content = '选项的分值不能大于题目的满分值'
                            break
                        elif (columns_index + 2) < cur_len:
                            columns_index += 2
                            options = df.values[row_index - 2, columns_index]
                        else:
                            break
                    else:
                        content = '单选的选项的分值要大于等于0'
                        break
                else:
                    content = '单选的选项的分值不能为空'
                    break

            if content:
                return Response({'message': f'第 {row_index} 行出错: {content}'},
                                status=status.HTTP_400_BAD_REQUEST)

            if columns_index <= 5:
                return Response({'message': f'第 {row_index} 行出错: 单选至少要有两个选项！'},
                                status=status.HTTP_400_BAD_REQUEST)

        questions_opntion_objs = []  # 保存问题选项对象，用于批量创建
        # 遍历查询获取问题对象
        question_count = 0
        for row in df.itertuples():
            question_count += 1
            row_index, question, is_require, score = \
                getattr(row, 'Index') + 2, getattr(row, '题目'), getattr(row, '是否必填'), getattr(row, '满分分值')
            if is_require == '是':
                is_require = True
            else:
                is_require = False

            question_obj = Question.objects.create(name=question, score=score,
                                                   category_id=category_id, module_id=module_id,
                                                   is_required=is_require, type=1, file=[])

            cur_index_data = df.values[row_index - 2]
            cur_len = len(cur_index_data)
            columns_index = 3
            options = df.values[row_index - 2, columns_index]
            num = 'A'
            while options:
                options_score = df.values[row_index - 2, columns_index + 1]
                questions_opntion_objs.append(QuestionOption(question=question_obj, num=num, file=[],
                                                             score=options_score, name=options))
                if columns_index + 2 < cur_len:
                    columns_index += 2
                    options = df.values[row_index - 2, columns_index]
                    num = chr(ord(num) + 1)
                else:
                    break

        QuestionOption.objects.bulk_create(questions_opntion_objs, ignore_conflicts=True)

        msg = f"导入题目成功，共导入 {question_count} 条数据"
        return Response({'message': msg})


# 问卷
class QuestionnaireViewSet(mixins.ListModelMixin,
                           mixins.CreateModelMixin,
                           mixins.DestroyModelMixin,
                           mixins.UpdateModelMixin,
                           mixins.RetrieveModelMixin,
                           viewsets.GenericViewSet):
    queryset = Questionnaire.objects.filter(is_active=True).select_related('account')
    filter_backends = (OrderingFilter, DjangoFilterBackend)
    serializer_class = QuestionnaireListSerializer
    filter_class = QuestionnaireFilter
    # permission_classes = [IsAuthenticated]
    pagination_class = Pagination
    ordering_fields = ('id')

    def filter_queryset(self, queryset):
        """ 动态使用 filter_class """
        if self.action in ['list']:
            for backend in list(self.filter_backends):
                queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

    def get_serializer_class(self):
        if self.action == 'create':
            return QuestionnaireCreateSerializer
        if self.action == 'retrieve':
            return QuestionnaireRetrieveSerializer
        if self.action == 'update' or self.action == 'partial_update':
            return QuestionnaireUpdateSerializer
        return QuestionnaireListSerializer

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()

        # 判断权限
        if not request.user.is_admin():
            module_codename = instance.module.codename
            if not request.user.had_permission(f'survey_questionnaire_delete_{module_codename}'):
                return Response({'message': '无权限进行该操作'})

        instance.is_active = False
        instance.save()
        return Response(status=status.HTTP_204_NO_CONTENT)

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            # 找出这些问卷下的相关题目和分类
            qs = QuestionnaireQuestion.objects \
                .filter(category__questionnaire__in=page) \
                .values('id', 'category_id', questionnaire_id=F('category__questionnaire_id'),
                        question_score=F('question__score'))
            questionnaire_qs_cnt = {}
            questionnaire_qs_score = {}
            for i in qs:
                if i['questionnaire_id'] not in questionnaire_qs_cnt:
                    questionnaire_qs_cnt[i['questionnaire_id']] = 0
                    questionnaire_qs_score[i['questionnaire_id']] = 0
                questionnaire_qs_cnt[i['questionnaire_id']] += 1
                questionnaire_qs_score[i['questionnaire_id']] += i['question_score']

            context = {'questionnaire_qs_cnt': questionnaire_qs_cnt, 'questionnaire_qs_score': questionnaire_qs_score}

            serializer = self.get_serializer(page, many=True, context=context)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 判断权限
        if not request.user.is_admin():
            module_id = serializer.initial_data['module']
            module_codename = Module.objects.filter(id=module_id).values_list('codename', flat=True).first()
            if not request.user.had_permission(f'survey_questionnaire_create_{module_codename}'):
                return Response({'message': '无权限进行该操作'})

        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()

        # 判断权限
        if not request.user.is_admin():
            module_codename = instance.module.codename
            if not request.user.had_permission(f'survey_questionnaire_update_{module_codename}'):
                return Response({'message': '无权限进行该操作'})

        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        return Response(serializer.data)

    @action(methods=['get'], detail=True, url_path='detail')
    def get_single_questions_template(self, request, pk=None):
        instance = self.get_object()
        serializer = QuestionnaireDetailSerializer(instance, many=False)
        return Response(serializer.data)


# 轮播图
class SwiperViewSet(mixins.CreateModelMixin,
                    mixins.DestroyModelMixin,
                    mixins.ListModelMixin,
                    viewsets.GenericViewSet):
    queryset = Swiper.objects.all().order_by('id').select_related('account')
    serializer_class = SwiperSerializer
    filter_backends = (OrderingFilter, DjangoFilterBackend)
    pagination_class = Pagination
    ordering_fields = ('id', 'created_time')
    filter_fields = ['is_active']

    def create(self, request, *args, **kwargs):
        if not request.user.is_admin() and not request.user.is_role('survey_admin'):
            return Response({'message': '您无权限进行新增！'}, status=status.HTTP_400_BAD_REQUEST)
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    # 修改状态，也就是启用或禁用
    @action(methods=['patch'], detail=True, url_path='change_status')
    def change_status(self, request, *args, **kwargs):
        if not request.user.is_admin() and not request.user.is_role('survey_admin'):
            return Response({'message': '您无权限进行该操作！'}, status=status.HTTP_400_BAD_REQUEST)
        instance = self.get_object()  # 获取要更新的那条数据
        new_status = request.data.get('is_active', None)
        if new_status not in [0, 1, True, False]:
            return Response({'message': '需要正确的状态'}, status=status.HTTP_400_BAD_REQUEST)
        instance.is_active = new_status
        instance.save()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

    def destroy(self, request, *args, **kwargs):
        if not request.user.is_admin() and not request.user.is_role('survey_admin'):
            return Response({'message': '您无权限进行该操作！'}, status=status.HTTP_400_BAD_REQUEST)
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)


class ModuleTeamViewSet(mixins.CreateModelMixin,
                        mixins.DestroyModelMixin,
                        mixins.ListModelMixin,
                        mixins.UpdateModelMixin,
                        viewsets.GenericViewSet):
    queryset = ModuleTeam.objects.all().order_by('-id').select_related('account')
    serializer_class = ModuleTeamSerializer
    filter_class = ModuleTeamFilter
    filter_backends = (OrderingFilter, DjangoFilterBackend)
    pagination_class = Pagination

    def filter_queryset(self, queryset):
        """ 动态使用 filter_class """
        if self.action in ['list']:
            for backend in list(self.filter_backends):
                queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()

        # 判断权限
        if not request.user.is_admin():
            module_codename = instance.module.codename
            if not request.user.had_permission(f'module_team_manage_{module_codename}'):
                return Response({'message': '无权限进行该操作'})

        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        return Response(serializer.data)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 判断权限
        if not request.user.is_admin():
            module_id = serializer.initial_data['module']
            module_codename = Module.objects.filter(id=module_id).values_list('codename', flat=True).first()
            if not request.user.had_permission(f'module_team_manage_{module_codename}'):
                return Response({'message': '无权限进行该操作'})

        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()

        # 判断权限
        if not request.user.is_admin():
            module_codename = instance.module.codename
            if not request.user.had_permission(f'module_team_manage_{module_codename}'):
                return Response({'message': '无权限进行该操作'})

        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)
